<?php
namespace App\Manager;
use App\Manager\Storage;
use App\Manager\Util;
use Exception;

/**
 * 任务管理类管理类
 * 任务使用hashs
 * 未明确 task 中是否可以使用sleep和curl
 */
class TaskManager extends BaseManager
{
    const PER_FIX = 'TASK:';

    const MAP_PER_FIX = 'TASK:MAP:';
    // 文档转换的服务器

    const WORK_SUCCESS = 0;

    const WORK_FINISH = 1;

    const DOC_URL = 'http://192.168.0.43:8080/pallasa_cloudteachzyf/whiteboard/api/pdf';

    const DIR_URL = 'http://192.168.0.43:8080/pallasa_cloudteachzyf/upload/pdf-png-data/pdf-png/';

    const DIR_PATH = '/home/zhengyifan/pallasa_cloudteachzyf/html/upload/pdf-png-data/pdf-png/';
    // 轮训查找间隔时间
    const TASK_SEARCH_TIME = 5;

    private $config = [];

    public function setConfig($config)
    {
        $this->config = $config;
    }

    /**
     * 获取任务状态 -1 不存在 0 未完成 1 已完成
     */
    public function workStatus($id)
    {
        $res = $this->getStorage()->hget(self::PER_FIX, $id);
        return false === $res ? -1 : $res;
    }

    /**
     * 添加任务
     */
    public function addWork($id)
    {
        // $res = $this->getStorage()->hsetnx(self::PER_FIX, $id, 0);
        // if (false === $res)
        // {
        //     return false;
        // }
        // 
        return $this->startWork($id);
    }

    /**
     * 开始任务
     * @return 文件夹路径
     */
    public function startWork($id)
    {
        if (Util::getfilecounts($this->config['whiteboard']['DIR_PATH'] . $id) > 0)
        {
            // 存在
            $this->finishWork($id);
            return true;
        }
        $res = $this->startDocWork($id);
        // doc转换已经结束，检查本地是否已存在文件
        if (Util::getfilecounts($this->config['whiteboard']['DIR_PATH'] . $id) > 0)
        {
            // 存在
            $this->finishWork($id);
            return true;
        }
        // 不存在，开始转换文档。。加锁
        $storage = $this->getStorage();
        // 已经开始转换, 等待时长15分钟
        $spend_time = time() + 15 * 60;
        var_dump('尝试获取锁！');
        if (!$storage->setnx($res['url'] . $id, $spend_time)) {
            var_dump('获取锁失败');
            // 在转换中
            $flag = 0;
            var_dump('开始等待解锁。。。。');
            while ($storage->exists($res['url'] . $id)) 
            {
                var_dump('等待解锁。。。。。');
                $val = $storage->get($res['url'] . $id);
                if (!$val || strlen($val) < 10) {
                    // 锁已被释放
                    break;
                }
                var_dump('time()', time());
                var_dump('$val', $val);
                if (+$val < time()) {
                    // 锁已经过期，删除,并自己获得锁， 任存在并发问题，但概率较小
                    $flag = 1;
                    $storage->set($res['url'] . $id, time() + 15 * 60);
                    break;
                }
                sleep(0.5);
            }
            var_dump('解锁成功！');
            var_dump('flag', $flag);
            if ($flag == 0) {
                // 转换完成
                $this->finishWork($id);
                return true;
            }
        }
        var_dump('获取锁成功！！！');
        $this->pdfToImg($res['url'], $id);
        if (!empty($res['animation']))
        {
            // 有动画的压缩文件，则直接处理动画，而不处理pdf
            $this->dealAnimation($res['animation'], $id);
        }
        // 解锁
        $storage->remove($res['url'] . $id);
        var_dump('释放锁');
        $this->finishWork($id);
        return true;
    }

    /**
     * 任务完成
     */
    public function finishWork($id)
    {
        return $this->getStorage()->hdel(self::PER_FIX, $id);
    }

    /**
     * @param id int 转换的文件id
     * @return result 
     *              code int 0 未结束， 1 已结束
     *              url string 文件地址
     */
    public function startDocWork($id)
    {
        // http请求，获取状态
        // limit_times = 
        while (1) 
        {
            $res = $this->checkDocWork($id);
            if ($res['result'] != self::WORK_SUCCESS)
            {
                throw new Exception($res['msg'], 1);
            }
            var_dump($id);
            var_dump($res);
            if ($res['status'] == self::WORK_FINISH)
            {
                return $res;
            }
            sleep(self::TASK_SEARCH_TIME);
        }
    }

    /**
     * 检查doc是否转换成功
     */
    public function checkDocWork($id)
    {
        $param = [
            'id' => $id
        ];
        $res = Util::post($this->config['whiteboard']['DOC_URL'], $param);
        return $res;
    }

    /**
     * 将id转换成图片url返回
     */
    public function transToUrl($id, $page=null)
    {
    	$file = $this->config['whiteboard']['DIR_PATH'] . $id . '/page_info.json';
    	$json_str = file_get_contents($file);
    	$json = json_decode($json_str, true);
        return $json;
    }

    /**
     * pdf转图片
     */
    public function pdfToImg($url, $id)
    {
        $file = $this->download($url);
        if (!file_exists($this->config['whiteboard']['DIR_PATH'] . $id))
        {
            mkdir($this->config['whiteboard']['DIR_PATH'] . $id, 0777, true);
        }
        var_dump('开始pdf转化！', date('Y-m-d H:i:s'));
        $result = $this->pdf2png($file, $this->config['whiteboard']['DIR_PATH'] . "{$id}/%d.png", '', ['resolution' => 96]);
        if ($result != 0)
        {
            var_dump('开始pdf转化（失败）！！！', date('Y-m-d H:i:s'));
            throw new Exception("转换失败！", 1);
        }
        var_dump('开始pdf转化成功！！！', date('Y-m-d H:i:s'));
        $dir = $this->config['whiteboard']['DIR_PATH'] . $id;
        $dir_hd = opendir($dir);
        $url = [];
        while (false !== ($file = readdir($dir_hd)))
        {
            if (is_file($dir . '/' . $file))
            {
                list($key) = explode('.', $file);
                $url[$key] = ['static' => trim($this->config['whiteboard']['DIR_URL'], '/') .'/'. $id . "/" . $file];
            }
        }
        file_put_contents($this->config['whiteboard']['DIR_PATH'] . $id . '/page_info.json', json_encode($url));
        return true;
    }

    public function dealAnimation($url, $id)
    {
        $zip_type = substr($url, strrpos($url, '.') + 1);
        $file = $this->download($url, ".{$zip_type}");
        // 解压到单独的文件夹中
        $_dir = $this->config['whiteboard']['DIR_PATH'] . $id;
        $dir = $_dir . '/animation';
        if (!file_exists($dir))
        {
            mkdir($dir, 0777, true);
        }
        chdir($dir);
        $result = $this->unzip($file, $zip_type);
        if ($result != 0)
        {
            return false;
        }
        $json_file = $dir . '/page_config.json';
        if (!file_exists($json_file))
        {
            return false;
        }
        $json_str = file_get_contents($json_file);
        $json = json_decode($json_str, true);
        if ($json === false)
        {
            return false;
        }
        // 提取需要的文件
        $page_list = [];
        $old_png = [];
        $page_json = [];
        for ($i=1; $i<=$json['total_page_count']; $i++)
        {
        	$page_json[$i] = [];
            if (!isset($json['animation_pages']["{$i}"]))
            {
                $old_png[$i] = count($page_list)+1;
                $page_json[$i]['static'] = trim($this->config['whiteboard']['DIR_URL'], '/') .'/'. $id . "/" . $old_png[$i] . '.png';
            }
            else
            {
            	$page_json[$i]['static'] = trim($this->config['whiteboard']['DIR_URL'], '/') .'/'. $id . "/" . (count($page_list)+1) . '.jpg';
            }
            $page = empty($json['animation_pages']["{$i}"]) ? null : $json['animation_pages']["{$i}"];
            $page_list[] = $page['static_files'][0];
            if (!empty($page) && is_array($page['animation_files']))
            {
                $page_json[$i]['animation'] = [];
                foreach ($page['animation_files'] as $k => $v)
                {
                	$page_json[$i]['animation'][$k + 1] = trim($this->config['whiteboard']['DIR_URL'], '/') .'/'. $id . "/" . (count( $page_list) + 1) . '.gif';
                    $page_list[] = $v;
                }
            }
        }
        krsort($old_png);
        foreach ($old_png as $k => $v)
        {
            @rename($_dir . "/{$k}.png", $_dir . "/{$v}.png");
        }
        foreach ($page_list as $k => $v)
        {
            if ($v)
            {
                $extend = substr($v, strrpos($v, '.'));
                if (file_exists($_dir . "/" . ($k+1) . ".png"))
                {
                    @unlink($_dir . "/" . ($k+1) . ".png");
                }
                @rename($dir . "/{$v}", $_dir . "/" . ($k+1) . $extend);
                @rename(str_replace('.gif', '.jpg', $dir . "/{$v}"), $_dir . "/" . ($k+1) . '.jpg');
            }
        }
        if (!empty($page_json))
        	file_put_contents($this->config['whiteboard']['DIR_PATH'] . $id . '/page_info.json', json_encode($page_json));
        return true;
    }

    /**
     * 
     */
    public function unzip($file, $type)
    {
        switch ($type)
        {
            case 'zip':
                $cmd = "unzip {$file}";
                break;
            case '7z':
                $cmd = "7za e {$file}";
                break;
            default :
                $cmd = "tar -xvf {$file}";
        }
        $str = system($cmd, $ret);
        return $ret;
    }

    /**
     * 将网络文件保存到本地
     */
    public function download($url, $type = '.pdf')
    {
        $tmp_dir = sys_get_temp_dir();
        $tmp_file = $tmp_dir . '/' . Util::uuid() . $type;
        $data = file_get_contents($url);
        if ($data === false)
            throw new Exception("获取pdf失败！", 1);
        $res = file_put_contents($tmp_file, $data);
        if ($res === false)
            throw new Exception("获取pdf失败！", 1);
        return $tmp_file;
    }

    /**
     * PDF2PNG
     * @param   stirng  $input_file
     * @param   string  $output_file    %d or %02d for pages
     * @param   string  $pages          1,2,3-5  etc. empty for all pages
     * @param   array   $config         configuration:
     *                  password    string
     *                  resolution  int     default to 72dpi
     *                  colorspace  string  default from input file
     *                  lowmemory   bool    true to clear cache for every page
     *                  threads     int     thread num
     */
    public function pdf2png($input_file, $output_file, $pages = '', $config = array())
    {
        $cmd = "mutool draw -o {$output_file} -i";
        if (isset($config['password']))
        {
            $cmd .= ' -p ' . $config['password'];
        }
        if (isset($config['resolution']))
        {
            $cmd .= ' -r ' . $config['resolution'];
        }
        if (isset($config['colorspace']))
        {
            $cmd .= ' -c ' . $config['colorspace'];
        }
        if (isset($config['lowmemory']) && $config['lowmemory'])
        {
            $cmd .= ' -L';
        }
        if (isset($config['threads']))
        {
            $cmd .= ' -T ' . $config['threads'];
        }
        if (strlen($pages) > 0)
        {
            $cmd .= ' ' . $pages;
        }
        $cmd .= ' ' . $input_file;
        $ret = -1;
        var_dump('exec', $cmd);
        $str = system($cmd, $ret);
        return $ret;
    }
}
